package com.github.hiling.gateway.filter;

import com.alibaba.fastjson2.JSON;
import com.github.hiling.common.constant.ServiceNames;
import com.github.hiling.common.exception.BusinessException;
import com.github.hiling.common.utils.HttpUtils;
import com.github.hiling.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;

/**
 * @author by hiling, Date on 10/15/2018.
 */
@Slf4j
@Component
@RefreshScope
public class AuthFilter implements GlobalFilter, Ordered {

    @Autowired
    HttpUtils httpUtils;

    public static final String ACCESS_TOKEN_ERROR = "accessToken无效或已过期。";

    @Value("${auth.ignore.path-prefix:/token,/link/}")
    private List<String> authIgnorePaths;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        ServerHttpRequest httpRequest = exchange.getRequest();

        String uri = httpRequest.getPath().value();

        //忽略不需要授权的连接
        for (String authIgnorePath : authIgnorePaths) {
            if (uri.startsWith(authIgnorePath)) {
                return chain.filter(exchange);
            }
        }

        String method = httpRequest.getMethod().name();

        log.debug("------------------->pre Request:{}:{}",method, uri);

        //内部应用通过jwt_token访问后端服务
        String jwtToken = httpRequest.getHeaders().getFirst("jwtToken");
        if (StringUtils.isNotEmpty(jwtToken)) {
            return chain.filter(exchange);
        }

        //外部应用通过access_token访问后端服务,需要使用access_token在OAuth Server上换取jwtToken后传递给后方服务
        String accessToken = httpRequest.getQueryParams().getFirst("accessToken");

        if (StringUtils.isEmpty(accessToken)) {
            throw new BusinessException(ACCESS_TOKEN_ERROR);
        }

        try {
            jwtToken = getJwtToken(accessToken);
        } catch (BusinessException e) {
            throw new BusinessException(e.getCode(), e.getMessage());
        }

        if (StringUtils.isEmpty(jwtToken)) {
            throw new BusinessException(ACCESS_TOKEN_ERROR);
        }
        httpRequest.mutate().header("jwtToken", jwtToken).build();
        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            // 这里是 response 时的过滤器，类似zuul中的FilterConstants.POST_TYPE;
            log.info("first post filter");
        }));
    }

    /**
     * 通过 @Order 来指定执行的顺序，数字越小，优先级越高
     * @return
     */
    @Override
    public int getOrder() {
        return 2;
    }

    private String getJwtToken(String accessToken) {
        return httpUtils.get("http://" + ServiceNames.AUTH_SERVICE + "/token?accessToken=" + accessToken);
    }
}